package com.stylefeng.guns.common.service.redis.Idgenerator;

import com.stylefeng.guns.common.service.redis.RedisClient;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * 通过redis获得缓存
 */
public class RedisUniqueNumberProducer implements UniqueNumberProducer, InitializingBean {

    private Logger log = LoggerFactory.getLogger(UniqueNumberProducer.class);


    private String cacheKey;//cache key in redis

    private IdGenConfig idGenConfig;

    private RedisClient redisClient;
    /**
     * 缓存keys
     */
    private Queue<Long> queue;

    public RedisUniqueNumberProducer(RedisClient redisClient, String cacheKey, IdGenConfig idGenConfig) {
        this.redisClient = redisClient;
        this.cacheKey = cacheKey;
        this.idGenConfig = idGenConfig;

    }


    /**
     * 获取下一唯一数字
     *
     * @return
     */
    @Override
    public long next() {
        long resp;
        if (!idGenConfig.isCache()) {
            resp = incrAndCompare(1);
        } else {
            Long val = queue.poll();
            if (val == null) {
                fullfillQueue(idGenConfig.getCacheSize());
                resp = next();
            } else {
                resp = val;
            }
        }
        return resp;
    }

    private void fullfillQueue(int size) {
        log.info("缓存耗尽,重新加载...");
        synchronized (queue) {
            CacheRange range = fetch(size);
            for (long i = range.getMin(); i < range.getMax(); i++) {
                queue.add(i);
            }
        }
        log.info("缓存耗尽,重新加载完毕...");

    }

    /**
     * 按偏移量增加,并视情况reset
     *
     * @param size
     * @return
     */
    private long incrAndCompare(int size) {
        long val = redisClient.incrBy(cacheKey, size);
        if (val > 9000000000000000000L) {//如果即将接近Long.MaxValue,重置cache
            resetCache();
        }

        return val;
    }

    /**
     * 获取缓存
     *
     * @param size
     * @return
     */
    private CacheRange fetch(int size) {
        Long val = incrAndCompare(size);

        return new CacheRange(val - size, val);
    }

    /**
     * 重置缓存
     */
    private void resetCache() {

        String lockKey = cacheKey + ".lock";

        //可以增加个临时锁,避免集群环境下的并发问题(因为会涉及del)
        String val = redisClient.get(lockKey, String.class);
        //if (val != null && !"".equals(val)) {
        if (StringUtils.isNotBlank(val)) {
            log.warn("其他进程/线程正在重置缓存中...本机不做处理...");
            return;
        }
        redisClient.set(lockKey, "true", 60L);//60秒超时
        redisClient.del(cacheKey);
        redisClient.del(lockKey);//reset key value
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        //if (idGenConfig == null || jodisClient == null || cacheKey == null || "".equals(cacheKey)) {
        if (idGenConfig == null || redisClient == null || StringUtils.isBlank(cacheKey)) {
            throw new Exception("idGenConfig/jodisClient/cacheKey can not be null");
        }
        if (idGenConfig.isCache()) {
            queue = new ConcurrentLinkedQueue<>();
        }
    }


}
